{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Seria Lab: add a new part to the inventory\n",
    "Adding new part to a squadron's inventory requires modifying attributes such as `m_id` and `m_master_id`. This is similar to adding ship to a fleet. But require less operations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "from copy import deepcopy\n",
    "from seria import *\n",
    "\n",
    "pf_path = 'sample/profile.seria'\n",
    "pf_node = load(pf_path)\n",
    "\n",
    "parts_path = 'sample/parts.seria'\n",
    "parts_node = load(parts_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we add a new part, we can list out existing parts in inventory. This is done by first locate player's squadron. And find the child node that is defined as the inventory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inventory m_id: 691432484788337130\n",
      "oid\t\tcount\tm_master_id\n",
      "MDL_QUARTERS_02\tNone\t691432484788337130\n",
      "MDL_FSS_02\t2\t691432484788337130\n",
      "ITEM_AMMO\t2\t691432484788337130\n",
      "ITEM_ARMOR\t2\t691432484788337130\n"
     ]
    }
   ],
   "source": [
    "# Player's squadron\n",
    "escadra_node = pf_node.get_node_if(\n",
    "    lambda n: n.get_attribute('m_name') == 'MARK')\n",
    "\n",
    "# Player's inventory\n",
    "inventory_node = escadra_node.get_node_if(\n",
    "    lambda n: n.header == 'm_inventory=7')\n",
    "\n",
    "id_inventory = inventory_node.get_attribute('m_id')\n",
    "print(f\"inventory m_id: {id_inventory}\")\n",
    "\n",
    "# list out all the parts in the inventory\n",
    "print('oid\\t\\tcount\\tm_master_id')\n",
    "\n",
    "for part in inventory_node.get_nodes():\n",
    "    oid = part.get_attribute('m_oid')\n",
    "    count = part.get_attribute('m_count')\n",
    "    master_id = part.get_attribute('m_master_id')\n",
    "\n",
    "    print(f'{oid}\\t{count}\\t{master_id}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Based on the output above, we can see that parts with multiple quantities have an attribute `m_count`, while parts with a quantity of one do not have this attribute. Similar to ships and other nodes that follow a hierarchical structure, these parts use the `master_id` attribute to indicate their relationship with the parent node above them. Therefore, when adding a new part, it is crucial to set the `master_id` correctly.\n",
    "1. Obtain all unique ids from the profile.\n",
    "2. Retrieve the selected part from the editor library node (`parts.seria`). Make a deep copy of it, so we can reuse the original node multiple times without repeating the parsing.\n",
    "3. Locate the target squadron's inventory\n",
    "4. Set part `m_id` to a newly generated id, and update relevant child nodes' attributes accordingly.\n",
    "5. Set part `m_master_id` to target inventory `m_id`\n",
    "6. Add part node to inventory node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_unique_ids(seria: SeriaNode) -> set:\n",
    "    unique_ids = set()\n",
    "\n",
    "    id = seria.get_attribute('m_id')\n",
    "    if id:\n",
    "        unique_ids.add(int(id))\n",
    "\n",
    "    for child in seria.get_nodes():\n",
    "        unique_ids.update(get_unique_ids(child))\n",
    "\n",
    "    return unique_ids"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# step 1\n",
    "ids = get_unique_ids(pf_node)\n",
    "\n",
    "# MDL_MISSILE_01 (R-5 ZENITH)\n",
    "# step 2\n",
    "missile_node = parts_node.get_node_if(\n",
    "    lambda n: n.get_attribute('m_oid') == 'MDL_MISSILE_01')\n",
    "m_node = deepcopy(missile_node)\n",
    "\n",
    "# step 4\n",
    "new_id = max(ids) + 1\n",
    "m_node.update_attribute('m_id', str(new_id))\n",
    "\n",
    "# step 5\n",
    "m_node.set_attribute('m_master_id', id_inventory)\n",
    "\n",
    "# if we want to add multiple missiles (optional)\n",
    "m_node.put_attribute_after('m_count', '5', 'm_oid')\n",
    "\n",
    "# step 6\n",
    "inventory_node.add_node(m_node)\n",
    "\n",
    "# save the modified profile\n",
    "pf_path2 = 'sample/profile2.seria'\n",
    "dump(pf_node, pf_path2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
